#ifndef MCMC_AFFINITY_MAP_H
#define MCMC_AFFINITY_MAP_H

#include "mcPoints.h"
#include "Vector.h"
#include "McLinkList.h"
#include "mcRand.h"
#include "mcmcBottomupCue.h"
#include "mcmcLowLevelStat4Affinity.h"


class MCMC_AtomicRegion;
class MCMC_GRegion;

///////////////////////////////////////////////
///////////////////////////////////////////////
class MCMC_Affinity
{
public:
	// construction & destruction
	MCMC_Affinity(){type=TOC_Intensity; q=0.0f;};

	// operators
	virtual MCMC_Affinity & operator=(const MCMC_Affinity &a)
	{
		if (this != &a)
			q = a.q;
		return *this;
	}
	bool operator==(const MCMC_Affinity &a){return q==a.q;}

	virtual MCMC_Affinity* CreateAndCopy(void) const=0;

	// operations
	virtual void	ComputeBond4Region(MCMC_LowLevelStat4Region &a, MCMC_LowLevelStat4Region &b)=0;

	virtual TypeOfCue	Type(void) const {return type;};

	virtual float	DistToQ(double dist);

public:
	float	q;

protected:
	TypeOfCue	type;
};

typedef MCMC_Affinity* PMCMC_Affinity;

///////////////////////////////////////////////
///////////////////////////////////////////////
class MCMC_Affinity_Intensity : public MCMC_Affinity
{
public:
	// operators
	virtual MCMC_Affinity* CreateAndCopy(void) const;

	// operations
	virtual void	ComputeBond4Region(MCMC_LowLevelStat4Region &a, MCMC_LowLevelStat4Region &b);
};


//////////////////////////////////////////////////
//////////////////////////////////////////////////
class MCMC_Affinity_Smooth : public MCMC_Affinity
{
public:
	// operators
	virtual MCMC_Affinity* CreateAndCopy(void) const;

	// operations
	virtual void	ComputeBond4Region(MCMC_LowLevelStat4Region &a, MCMC_LowLevelStat4Region &b);
};


///////////////////////////////////////////////
///////////////////////////////////////////////
class MCMC_Affinity_Texture : public MCMC_Affinity
{
public:
	// operators
	virtual MCMC_Affinity* CreateAndCopy(void) const;

	// operations
	virtual void	ComputeBond4Region(MCMC_LowLevelStat4Region &a, MCMC_LowLevelStat4Region &b);
};


///////////////////////////////////////////////
///////////////////////////////////////////////
class MCMC_CombinedAffinity
{
public:
	// construnction & destruction
	MCMC_CombinedAffinity();
	~MCMC_CombinedAffinity();
	void	Free(void);

	// operators
	MCMC_CombinedAffinity & operator=(const MCMC_CombinedAffinity &a);
	inline void		AddFrom(MCMC_CombinedAffinity &a);
	inline void		Init(const float q1);

	// operations
	void	ExpandSpaceTo(const int max_space_new);
	inline int	MaxSpace(void) const {return max_space;};
	inline int	Num(void) const {return num;};
	virtual void	ComputeAffinitiesOnAllCues(MCMC_LowLevelStat4Region &a, MCMC_LowLevelStat4Region &b);
	inline float	Q(void) const {return q;};
	inline void		TurnOnEdge(void){e=1;};
	inline void		TurnOffEdge(void){e=0;};
	inline char		Edge(void){return e;};
	inline MCMC_Affinity* GetCue(const int cue_type);
	virtual void	SampleEdge(RF &rf_on_off);
	inline void		Clear(void);

	enum MethodOfCombiningCues
	{
		MOCC_biggest=1,
		MOCC_product=2
	};

	virtual void	ComputeCombinedQ(mcmcVector<int> &vc_cue_type, MethodOfCombiningCues flag_method);

protected:
	float		q;
	char		e;

protected:
	PMCMC_Affinity	*p_cues;
	int		num;
	int		max_space;
};

inline void MCMC_CombinedAffinity::AddFrom(MCMC_CombinedAffinity &a)
{
	MCMC_Affinity	*pcue1,*pcue2;

	for (int i=0; i<Num(); i++)
	{
		pcue1 = GetCue(i);
		pcue2 = a.GetCue(i);

		if (pcue1!=NULL && pcue2!=NULL)
			pcue1->q += pcue2->q;
	}
}

inline void	MCMC_CombinedAffinity::Init(const float q1)
{
	MCMC_Affinity	*pcue1;

	for (int i=0; i<Num(); i++)
	{
		pcue1 = GetCue(i);

		if (pcue1!=NULL)
			pcue1->q = q1;
	}
}


inline void	MCMC_CombinedAffinity::Clear(void)
{
	for (int i=0; i<num; i++)
		if (p_cues[i] != NULL)
			p_cues[i]->q = 0.0f;
	q = 0.0f;
}


typedef MCMC_CombinedAffinity* PMCMC_CombinedAffinity;

inline MCMC_Affinity* MCMC_CombinedAffinity::GetCue(const int cue_type)
{
	if (cue_type<0 || cue_type>num)
		return NULL;
	else
		return p_cues[cue_type];
}


//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
class MCMC_ArrayManagement
{
public:
	// construction & destruction
	MCMC_ArrayManagement();
	~MCMC_ArrayManagement();

	virtual void	Empty(void);

	// operators
	MCMC_ArrayManagement &	operator=(const MCMC_ArrayManagement &a);

	// operations
	virtual void	Init(const int max_num);
	virtual void	ExpandTo(const int max_new);


	virtual int		New(void);
	virtual int		New(const int id);
	virtual bool	Delete(const int id);

	inline	int		Num(void) const {return num_of_active;};
	inline	int		Max(void) const {return vc_next.size();};
	inline	int		GetFirst(void);
	inline  int		GetFirstInRecyle();
	inline	int		GetNext(void);
	inline  int		GetCurrent(void){return current;};
	inline	bool	Valid(const int id){return (id>=0 && id<vc_next.size());}

protected:
	virtual void	CreateRecord(int id);

protected:
	int				head_active,tail_active;
	int				num_of_active;		// number of active nodes
	int				head_recyle;
	int				current;

private:
	mcmcVector<int>	vc_next;
	mcmcVector<int>	vc_last;
	mcmcVector<char>	vc_sign;
};

inline int	MCMC_ArrayManagement::GetFirst(void)
{
	current = head_active;
	return current;
}

inline  int	MCMC_ArrayManagement::GetFirstInRecyle()
{
	current = head_recyle;
	return current;
}

inline int	MCMC_ArrayManagement::GetNext(void)
{
	if (current<0 || current>=Max())
		return -1;
	else
	{
		current = vc_next.Data(current);
		return current;
	}
}


//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
// affinities of one element w.r.t. all other elements
//  use a hash table here
class MCMC_AffinityMap
{
public:
	// construnction & destruction
	MCMC_AffinityMap();
	~MCMC_AffinityMap();
	void Free(void);
	void Empty(void);

	// operators
	MCMC_AffinityMap & operator=(const MCMC_AffinityMap &a);

	// operations
	MCMC_CombinedAffinity*	GetAffinity(const int id);
	MCMC_CombinedAffinity*	NewAffinity(const int id);
	bool				DeleteAffinity(const int id);
	inline	int			HashIndex(const int id) {return id%max_space;};
	inline	int		Num(void) const {return mag_hash.Num()+mag_extn.Num();};
	inline  int		MaxSpace(void) const {return max_space;};

	virtual void		PrepareBondsWith(mcmcVector<int> &vc_cue_type, mcmcVector<int> &vc_indexid);
	virtual void		PrepareBondsMasked(mcmcVector<int> &vc_cue_type, mcmcVector<int> *pvc_mask_indexid=NULL);

	virtual void		SampleEdges(mcmcMatrix<char> &mx_sampled_edges, RF &rf_on_off,
									const int indexid1, mcmcVector<int> *pvc_mask_indexid=NULL);

	virtual void		RemoveEdges(void);

	inline MCMC_CombinedAffinity*	GetFirstCombinedCue(int* pindexid=NULL);
	inline MCMC_CombinedAffinity*	GetNextCombinedCue(int* pindexid=NULL);

protected:
	void	ExpandTo(const int max_space_new);


protected:
	PMCMC_CombinedAffinity	*p_combined_cues_hashed;
	mcmcVector<int>		vc_id_hashed;
	PMCMC_CombinedAffinity	*p_combined_cues_extn;
	mcmcVector<int>		vc_id_extn;

	int					max_space;

	MCMC_ArrayManagement	mag_hash,mag_extn;

private:
	char				sign_move;
};


MCMC_CombinedAffinity* MCMC_AffinityMap::GetFirstCombinedCue(int* pindexid)
{
	int index;

	sign_move = 0;
	index = mag_hash.GetFirst();
	if (index >= 0)
	{
		if (pindexid!=NULL)
			*pindexid = vc_id_hashed.Data(index);
		return p_combined_cues_hashed[index];
	}
	else
	{
		sign_move = 1;

		index = mag_extn.GetFirst();
		if (index >= 0)
		{
			if (pindexid!=NULL)
				*pindexid = vc_id_extn.Data(index);
			return p_combined_cues_extn[index];
		}
		else
			return NULL;
	}
}

MCMC_CombinedAffinity* MCMC_AffinityMap::GetNextCombinedCue(int* pindexid)
{
	int index;

	if (sign_move == 0)
	{
		// still in the hash table

		index = mag_hash.GetNext();
		if (index >= 0)
		{
			if (pindexid!=NULL)
				*pindexid = vc_id_hashed.Data(index);
			return p_combined_cues_hashed[index];
		}
		else
		{
			sign_move = 1;

			index = mag_extn.GetFirst();
			if (index >= 0)
			{
				if (pindexid!=NULL)
					*pindexid = vc_id_extn.Data(index);
				return p_combined_cues_extn[index];
			}
			else
				return NULL;
		}
	}
	else
	{
		index = mag_extn.GetNext();
		if (index >= 0)
		{
			if (pindexid!=NULL)
				*pindexid = vc_id_extn.Data(index);
			return p_combined_cues_extn[index];
		}
		else
			return NULL;
	}
}

#endif
